﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace BeachRunner.Tileengine
{
    /// <summary>
    /// Eine Klasse zum darstellen einer Ebene.
    /// 
    /// Autor: OLD (Mattis Hänel)
    /// </summary>
    public class Viewport
    {
        /// <summary>
        /// Die Breite der darzustellenden Ebene
        /// </summary>
        private int layerWidth;
        /// <summary>
        /// Macht die Breite des darstzustellenden Layers zugänglich.
        /// </summary>
        public int LayerWidth
        {
            get { return layerWidth; }
            set { layerWidth = value; }
        }

        /// <summary>
        /// Die Höhe der darzustellenden Ebene
        /// </summary>
        private int layerHeight;
        /// <summary>
        /// Macht die Höhe des darstzustellenden Layers zugänglich.
        /// </summary>
        public int LayerHeight
        {
            get { return layerHeight; }
            set { layerHeight = value; }
        }
	

	    /// <summary>
	    /// Die Breite des Viewports in [px].
	    /// </summary>
	    private int screenWidth;

        /// <summary>
        /// Die Höhe des Viewports in [px].
        /// </summary>
        private int screenHeight;


        /// <summary>
        /// Die halbe Breite des Viewports in [px].
        /// </summary>
	    private int halfWidth;
    
        /// <summary>
        /// Die halbe Höhe des Viewports in [px].
        /// </summary>
        private int halfHeight;
	

        /// <summary>
        /// Die Kantenlänge einer Kachel in [px].
        /// </summary>
	    private const int tileSize = Game1.TILE_SIZE;

        /// <summary>
        /// Die Anzahl der Kacheln die in der Höhe auf den Bildschrim passen.
        /// </summary>
        private int tilesPerWidth;

        /// <summary>
        /// Die Anzahl der Kacheln die in der Breite auf den Bildschrim passen.
        /// </summary>
        private int tilesPerHeight;


        /// <summary>
        /// Die Position auf der Karte die im Zentrum des Viewports liegen soll.
        /// </summary>
        private Vector2 scroll;
        /// <summary>
        /// Macht die Position auf der Karte die im Zentrum des Viewports liegt zugänglich.
        /// Siehe Dokumetation ( S. __ )
        /// </summary>
        public Vector2 Scroll
        {
            get { return scroll; }
            set { scroll = value; }
        }

        /// <summary>
        /// Die X Koordinate der Linken oberen Viewportecke auf der Karte.
        /// Siehe Dokumetation ( S. __ )
        /// </summary>
	    private int cornerX = 0;
        /// <summary>
        /// Die Y Koordinate der Linken oberen Viewportecke auf der Karte.
        /// Siehe Dokumetation ( S. __ )
        /// </summary>
	    private int cornerY = 0;
	
        /// <summary>
        /// Gibt an um wie viele Pixel die Darstellung auf der X-Achse verschoben werden muss.
        /// Siehe Dokumetation ( S. __ )
        /// </summary>
	    private int offsetX = 0;
        /// <summary>
        /// Gibt an um wie viele Pixel die Darstellung auf der X-Achse verschoben werden muss.
        /// Siehe Dokumetation ( S. __ )
        /// </summary>
	    private int offsetY = 0;

        /// <summary>
        /// Gibt an von welcher Kachelspalte die Darstellung anzufangen ist.
        /// </summary>
	    private int tileOffsetX = 0;

        /// <summary>
        /// Gibt an von welcher Kachelzeile die Darstellung anzufangen ist.
        /// </summary>
	    private int tileOffsetY = 0;
	
        /// <summary>
        /// Erstellt einen neuen Viewport.
        /// </summary>
        /// <param name="width">Breite des Viewports in Pixel</param>
        /// <param name="height">Höhe des Viewports in Pixel</param>
        /// <param name="layerWidth">Breite der darzustellenden Ebene</param>
        /// <param name="layerHeight">Höhe der darzustellenden Ebene</param>
	    public Viewport( int width, int height, int layerWidth, int layerHeight ) {
		    SetSize( width, height );

            this.layerWidth = layerWidth;
            this.layerHeight = layerHeight;
	    }
	
        /// <summary>
        /// Legt die Dimensionen des Viewports fest.
        /// </summary>
        /// <param name="width">Breite des Viewports in Pixel</param>
        /// <param name="height">Höhe des Viewports in Pixel</param>
	    public void SetSize( int width, int height ) {
		    screenWidth = width;
		    screenHeight = height;
		
		    halfWidth = screenWidth / 2;
		    halfHeight = screenHeight / 2;
		
		    tilesPerWidth = screenWidth / tileSize + 2;
		    tilesPerHeight = screenHeight / tileSize + 2;
	    }

        /// <summary>
        /// Liefert den Index des Tiles zurück der sich an der Position (screenX; screenY) befindet.
        /// </summary>
        /// <param name="screenX">X-Koordinate auf dem Viewport</param>
        /// <param name="screenY">Y-Koordinate auf dem Viewport</param>
        /// <returns>Der Index des Tiles an der Position (screenX; screenY)</returns>
	    public int GetTileAtScreenPos( int screenX, int screenY ) {
		    return (cornerX + screenX) / tileSize + ((cornerY + screenY) / tileSize * layerWidth);;
	    }

        /// <summary>
        /// Liefert den Index und die Kartenpostion des Tiles zurück der sich an der Position (screenX; screenY) befindet.
        /// </summary>
        /// <param name="x">Die X-Koordinate der Kartenposition</param>
        /// <param name="y">Die Y-Koordinate der Kartenposition</param>
        /// <param name="screenX">Die X-Koordinate auf dem Viewport</param>
        /// <param name="screenY">Die Y-Koordinate auf dem Viewport</param>
        /// <returns>Der Index des Tiles an der Position (screenX; screenY)</returns>
        public int GetTileAtScreenPos(out int x, out int y, int screenX, int screenY)
        {
            x = (cornerX + screenX) / tileSize;
            y = (cornerY + screenY) / tileSize;

            return x + y * layerWidth;
        }

        /// <summary>
        /// Rendert eine Ebene des Viewports auf den zugehörigen SpriteBatch.
        /// </summary>
        /// <param name="batch">Der SpriteBatch auf dem gezeichnet werden soll.</param>
        /// <param name="layerData">Die Ebenendaten</param>
        /// <param name="tileset">Das zum darstellen zu verwendende Tileset</param>
        public void Draw(SpriteBatch batch, Layer layer, Tileset tileset)
        {
            cornerX = (int)scroll.X - screenWidth / 2;
            cornerY = (int)scroll.Y - screenHeight / 2;

            tileOffsetX = cornerX / tileSize;
            tileOffsetY = cornerY / tileSize;

            offsetX = cornerX % tileSize;
            offsetY = cornerY % tileSize;

            int indexMap, indexTileset;

            Vector2 pos = new Vector2();

            for (int ix = 0; ix < tilesPerWidth; ix++)
            {
                for (int iy = 0; iy < tilesPerHeight; iy++)
                {
                    if (tileOffsetX + ix >= 0 && tileOffsetX + ix < layer.Width &&
                    tileOffsetY + iy >= 0 && tileOffsetY + iy < layer.Height)
                    {
                        indexMap = (tileOffsetY + iy) * layer.Width + tileOffsetX + ix;
                        indexTileset = layer.Data[indexMap];

                        if (indexTileset > 0)
                        {
                            pos.X = (ix * tileSize - offsetX);
                            pos.Y = (iy * tileSize - offsetY);

                            batch.Draw(tileset.Texture, pos, tileset.SourceRectangles[indexTileset - 1], Color.White);
                        }
                    }
                }
            }
        }
    }
}
